﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Noris.Schedule.Support;
using System.Drawing;
using Noris.Schedule.Support.Services;
using Noris.Schedule.Support.Data;

namespace Noris.Schedule.UI.Components
{
	#region CLASS GridDataRowCls : Správce dat jednoho řádku. Trvalá data o řádku, data elementů vstupní, data elementů vizuální, i filtrovaná časově.
	/// <summary>
	/// GridDataRowCls : Správce dat jednoho řádku. Trvalá data o řádku, data elementů vstupní, data elementů vizuální, i filtrovaná časově.
	/// </summary>
	internal class GridDataRowCls
	{
		#region KONSTRUKCE
		private GridDataRowCls(IDataRow rowItem, GridDataCls parent)
		{
			this.RowItem = rowItem;
			this.Parent = parent;
			this._ElementDict = new Dictionary<GID, GridDataChartElementCls>();
			this._ElementAllList = null;
			this._SetDefaultProperty();
		}
		/// <summary>
		/// Nastaví defaultní hodnoty do property
		/// </summary>
		private void _SetDefaultProperty()
		{
			this.ChartElementLoadedTimeRange = TimeRange.Empty;
		}
		/// <summary>
		/// Statický konstruktor
		/// </summary>
		/// <param name="rowItem"></param>
		/// <param name="parent"></param>
		/// <returns></returns>
		internal static GridDataRowCls Create(IDataRow rowItem, GridDataCls parent)
		{
			GridDataRowCls dataRow = new GridDataRowCls(rowItem, parent);
			dataRow.ChartElementsAdd(rowItem.ElementList, rowItem.AllElementsLoaded);
			return dataRow;
		}
		/// <summary>
		/// Soupis elementů, vytvořených ze vstupních dat.
		/// Každý unikátní GID je zde jen jednou.
		/// </summary>
		private Dictionary<GID, GridDataChartElementCls> _ElementDict { get; set; }
		/// <summary>
		/// Soupis všech položek, vytvořený ze vstupních dat, setříděný dle data počátku a umístěný na relativní pozice Top, Bottom
		/// </summary>
		private List<GridDataChartElementCls> _ElementAllList;
		/// <summary>
		/// Soupis aktuálně platných souřadnic jednotlivých linek výroby (oddělují celočíselné linky)
		/// </summary>
		private List<int> _UnitLineList;
		/// <summary>
		/// Soupis aktuálně viditelných položek, s ohledem na viditelné období a rozměr. 
		/// Prvky mají přidělen jejich Rectangle (fyzická souřadnice), s bodem počátku 0/0 = levý horní roh viditelné oblasti řádku.
		/// </summary>
		private List<GridDataChartElementCls> _ElementVisibleList;
		#endregion
		#region PROPERTY
		/// <summary> { get; }
		/// Reference na Parent objekt = data grafu
		/// </summary>
		internal GridDataCls Parent { get; private set; }
		/// <summary> { get; }
		/// Data položky, získaná z datového zdroje
		/// </summary>
		internal IDataRow RowItem { get; private set; }
		/// <summary>
		/// Druh grafu, který je pro tento řádek zobrazen
		/// </summary>
		internal RowGraphMode RowGraph { get { return this.RowItem.RowGraph; } }
		/// <summary> { get; }
		/// GID tohoto záznamu = řádek.
		/// </summary>
		internal GID GId { get { return this.RowItem.GId; } }
		/// <summary>
		/// Počet načtených elementů, bez dokalkulací = jen pro programovou vizualizaci.
		/// Ve formě počtu: "25/130" (viditelných / celkem).
		/// </summary>
		internal string ElementCount
		{
			get
			{
				string vis = (this._ElementVisibleList == null ? "???" : this._ElementVisibleList.Count.ToString());
				string all = (this._ElementAllList == null ? "???" : this._ElementAllList.Count.ToString());
				return vis + "/" + all;
			}
		}
		/// <summary> { get; }
		/// Soupis vizuálních elementů.
		/// Obsahuje tolik prvků, kolik je prvků ve slovníku DataItemDict.
		/// Vždy obsahuje korektně určené souřadnice Top a Bottom, řadí prvky odspodu (Bottom = 0) nahoru.
		/// Nejvyšší vypočtená pozice Top je uložena v this.TopMax.
		/// Upozornění: elementy, které jsou v tomto seznamu, nemusí být všechny viditelné.
		/// Všechny elementy mají korektně vypočtenou základní dimenzi (Rectangle), 
		/// ale neviditelné elementy nemusí mít vypočteny rozšiřující interní dimenze.
		/// Pokud se aplikace chce spolehnout na platnost všech dimenzí všech elementů, 
		/// nechť na elementu zavolá metodu GridDataChartElementCls.InternalDimensionCheck().
		/// Tato metoda zajistí kontrolu a v případě potřeby přepočet interních dimenzí.
		/// </summary>
		internal List<GridDataChartElementCls> ElementAllList
		{
			get
			{
				this._ElementAllListCheck();
				return this._ElementAllList;
			}
		}
        /// <summary>
        /// Počet všech elementů v tomto řádku.
        /// Počet je zjištěn okamžitě, na rozdíl od použití property ElementAllList, která nejprve provede korektní přípravu dat.
        /// </summary>
        internal int ElementAllListCount { get { return this._ElementDict.Count; } }
		/// <summary> { get; }
		/// Soupis pozic Top, na nichž se mají kreslit vodorovné čáry, oddělující jednotlivé Lines.
		/// Souřadnice jsou v pixelech.
		/// </summary>
		internal List<int> CurrentLineList
		{
			get
			{
				this._UnitLineListCheck();
				return this._UnitLineList;
			}
		}
		/// <summary>
		/// Obsahuje (vrací) příznak, že soupis elementů this.ElementAllList je validní
		/// (obsahuje skutečně všechny elementy, a je určena jejich relativní pozice a je určena výška řádku).
		/// Pokud je tato property false, pak čtením jakýchkoli dat elementů dojde k jejich přepočtu a k možné změně výšky řádku (this.RowHeightPx).
		/// Naproti tomu čtení tohoto příznaku IsAllListValid nezpůsobí žádnou změnu.
		/// </summary>
		internal bool IsAllListValid { get { return !this._ElementAllListNeedPrepare; } }
		/// <summary>
		/// Obsahuje (vrací) příznak, že soupis elementů this.ElementVisibleList je validní
		/// (všechny jeho elementy mají korektní pozici, řádek má korektně určenou výšku).
		/// Poznámka: čtením seznamu this.ElementVisibleList může dojít k přepočtu pozic elementů a ke změně výšky řádku.
		/// Stejně tak čtením dat konkrétního elementu.
		/// Naproti tomu čtení tohoto příznaku IsVisibleListValid nezpůsobí žádnou změnu.
		/// </summary>
		internal bool IsVisibleListValid { get { return !this._ElementVisibleListNeedPrepare; } }
		/// <summary> { get; }
		/// Soupis aktuálně viditelných vizuálních elementů.
		/// Obsahuje jen ty položky, jejichž čas je alespoň zčásti uvnitř viditelného časového intervalu this.VisibleTimeRange
		/// </summary>
		internal List<GridDataChartElementCls> ElementVisibleList
		{
			get
			{
				this._CurrentVisualItemListCheck();
				return this._ElementVisibleList;
			}
		}
		/// <summary> { get; }
		/// Objekt, který obsahuje metody použitelné pro přepočty vizuálních a časových dat na časové ose (datum - pixel).
		/// Jde o referenci na objekt časové osy, která interně implementuje objekt časové kalkulačky i pro své účely.
		/// Poskytuje přístup k tomuto objektu přes interface ITimeAxisCalc, přes které lze přepočítávat souřadnice.
		/// </summary>
		internal ITimeAxisCalc TimeAxisCalc { get { return this.Parent.TimeAxisCalc; } }

		/// <summary> { get; set; }
		/// Externí požadavek na přepočet výšky a svislé pozice položek, a výšky řádku.
		/// Bude proveden při příštím čtení dat.
		/// Po provedení přepočtu bude nastaven na false.
		/// </summary>
		internal bool RequestCalcVertical { get; set; }
		/// <summary> { get; set; }
		/// Externí požadavek na přepočet pozic X a aktuální výběr elementů dle viditelného data.
		/// Bude proveden při příštím čtení dat.
		/// Po provedení přepočtu bude nastaven na false.
		/// </summary>
		internal bool RequestCalcHorizontal { get; set; }
		/// <summary>
		/// Příznak, že se liší hodnota RowHeightPx od RowHeightLastAcceptedPx = došlo ke změně výšky řádku od výšky posledně akceptované.
		/// </summary>
		internal bool IsRowHeightChanged { get { return (this.RowHeightLastAcceptedPx != this.RowHeightPx); } }
		/// <summary> { get; set; }
		/// Výška řádku, kterou posledně akceptovaly externí aplikace.
		/// Je zde k dispozici výlučně pro externí použití, řádek jako takový tuto hodnotu nenastavuje.
		/// Externí aplikace si sem může uložit hodnotu RowHeightPx, pro kterou naposledy vypočítala pozice Top řádku.
		/// Existuje zde property this.IsRowHeightChanged, která signalizuje změnu (this.RowHeightLastAcceptedPx != this.RowHeightPx).
		/// </summary>
		internal int RowHeightLastAcceptedPx { get; set; }
		/// <summary> { get; }
		/// Aktuálně viditelná výška řádku.
		/// Pozor, tato hodnota je spolehlivá. Čtení této hodnoty může způsobit přepočet dat na řádku, pokud v okamžiku čtení nejsou data platná.
		/// Výška řádku se typicky mění interně, po načtení dat a přepočtu pozic jednotlivých elementů. 
		/// Nastaví se sem vypočtená = požadovaná výška řádku.
		/// Výpočet: řádek musí zobrazit všechny svoje položky (všechny, nejen neviditelné), plus část řádku navíc (viz property AddLines).
		/// Položky jsou rozmístěny na výšku podle jejich hodnoty UseRatio, tak aby se nepřekrývaly, a co nejvíce seděly u spodního kraje řádku.
		/// </summary>
		internal int RowHeightPx
		{
			get
			{
				this._ElementAllListCheck();
				return this._RowHeightPx;
			}
		}
		/// <summary> { get; }
		/// Aktuálně viditelná výška řádku.
		/// Pozor: tato hodnota odráží posledně platnou hodnotu, ale pokud se od poslední aktualizace dat něco změnilo
		/// (přibyly nové položky, nebo jiné ubyly, nebo se změnily deklarované výšky řádku, atd), pak tato hodnota neodpovídá aktuálnímu stavu.
		/// </summary>
		internal int RowHeightUncalculated
		{
			get
			{
				return this._RowHeightPx;
			}
		}
		private int _RowHeightPx;
		/// <summary>
		/// Aktuálně použitá výška jedné linky. Běžně je rovna this.LineHeightPx.
		/// Pokud je ale definována maximální výška řádku this.RowHeightMaxPx,
		/// a aktuální stav dat by překročil výšku řádku this.RowHeightMaxPx,
		/// pak je výška this.LineHeightPx přepočtena tak aby odpovídala this.RowHeightMaxPx
		/// a úměrně k tomu je upravena i hodnota this.LineHeightCurrPx.
		/// </summary>
		internal int LineHeightCurrPx { get; private set; }
		/// <summary>
		/// Aktuální počet Lines, s přidáním AddLinesRatio. V poměru, ne v pixelech.
		/// </summary>
		internal decimal LineHeightCurrRatio { get; private set; }
		/// <summary>
		/// Největší hodnota souřadnice Top (v hodnotách UseRatio) ze všech vizuálních prvků v poli this.VisualItemList
		/// </summary>
		internal decimal UseRatioSumMax { get; private set; }
		/// <summary>
		/// Nejmenší hodnota UseRatio ze všech prvků v poli this.VisualItemList (může pomoct určit LineHeight)
		/// </summary>
		internal decimal UseRatioOneMin { get; private set; }
		/// <summary>
		/// true, pokud tento řádek obsahuje zlomkové elementy (jejichž relativní výška není rovna 1.00)
		/// Pro takové řádky je vhodné vykreslit vodorovné linky, které znázorňují jednotky (jeden stroj).
		/// </summary>
		internal bool ContainFragments { get; private set; }
		/// <summary>
		/// Příznak, že tento řádek obsahuje podílové využití kapacit (UseRatio je přinejmenším v jednom elementu menší než 1)
		/// </summary>
		internal bool ContainUseRatio { get { return (this.UseRatioOneMin > 0M && this.UseRatioOneMin < 1M); } }
		#endregion
        #region Property z deklarace řádku nebo z deklarace grafu (typ, výška, barva)
        /// <summary>
        /// true = řádek má vlastní objekt deklarace (this.RowItem.RowGraphDeclaration)
        /// </summary>
        private bool _HasExplicitDeclaration { get { return (this.RowItem != null && this.RowItem.RowGraphDeclaration != null); } }
        /// <summary>
        /// Deklarace explicitních vlastností řádku
        /// </summary>
        private DataRowGraphDeclaration _RowDeclaration { get { return (this.RowItem != null ? this.RowItem.RowGraphDeclaration : null); } }
        /// <summary>
        /// Barva pozadí řádku
        /// </summary>
        internal Color RowColor { get { return (this._HasExplicitDeclaration && !this._RowDeclaration.RowColor.IsEmpty ? this._RowDeclaration.RowColor : this.Parent.RowColor); } }
        /// <summary> { get; }
        /// Nejvyšší výška řádku. Pokud by výpočet určil vyšší hodnotu, bude řádek zmenšen na tuto výšku, a přepočten.
        /// Pokud bude uvedena hodnota 0, nebude se výška řádku kontrolovat.
        /// Hodnoty menší a rovno 10 budou rovněž ignorovány, z praktických důvodů zobrazování.
        /// </summary>
        internal int RowHeightTotalMaxPx { get { return (this._HasExplicitDeclaration && this._RowDeclaration.RowHeightTotalMaxPx > 0 ? this._RowDeclaration.RowHeightTotalMaxPx : this.Parent.RowHeightTotalMaxPx); } }
        /// <summary>
        /// Nejmenší výška řádku grafu, v pixelech.
        /// </summary>
        internal int RowHeightTotalMinPx { get { return (this._HasExplicitDeclaration && this._RowDeclaration.RowHeightTotalMinPx > 0 ? this._RowDeclaration.RowHeightTotalMinPx : this.Parent.RowHeightTotalMinPx); } }
        /// <summary> { get; }
        /// Požadovaná výška jedné linky řádku.
        /// Pokud se data v řádku nevejdou do celkové výšky RowHeightTotalMaxPx, 
        /// bude výška linky zmenšena (a uložena do DataRow.LineHeightCurrPx)
        /// tak, aby výška řádku odpovídala RowHeightTotalMaxPx.
        /// Platná hodnota je 10 až 100 pixelů. Jiné hodnoty budou zarovnány do těchto mezí.
        /// </summary>
        internal int RowHeightLineSolidPx { get { return (this._HasExplicitDeclaration && this._RowDeclaration.RowHeightLineSolidPx > 0 ? this._RowDeclaration.RowHeightLineSolidPx : this.Parent.RowHeightLineSolidPx); } }
        /// <summary>
        /// Typická výška jednoho řádku, pokud jde o řádek typu RowGraphMode.TaskCapacityLink, 
        /// a na tomto řádku je detekováno podílové čerpání kapacity (UseRatio je menší než 1).
        /// Systém sám detekuje takové řádky, a vykresluje je v této výšce linky namísto výšky RowHeightOneLine.
        /// Jde opět o výšku pro jeden stroj. Pokud bude v rámci kapacitní jednotky použito více (N) strojů, bude výška řádku N× vyšší.
        /// Implicitní hodnota je 60 pixelů.
        /// Platná hodnota je 7 až 100 pixelů. Jiné hodnoty budou zarovnány do těchto mezí.
        /// </summary>
        public int RowHeightLineFragmentPx { get { return (this._HasExplicitDeclaration && this._RowDeclaration.RowHeightLineFragmentPx > 0 ? this._RowDeclaration.RowHeightLineFragmentPx : this.Parent.RowHeightLineFragmentPx); } }
        /// <summary>
        /// Výška nejmenšího zlomku v grafu typu RowGraphMode.TaskCapacityLink, kdy jsou zobrazeny položky využívající podíl kapacity.
        /// Pokud například operace využije 1/10 kapacity (plochy) stroje, a výška linky (RowHeightOneFragmentedLine) bude 30 pixelů, 
        /// pak by výška využití této 1/10 kapacity byla 3 pixely. Což je téměř neviditelné.
        /// Proto je možno deklarovat výšku minimálního zlomku: pokud v takovém případě bude RowHeightMinFragment = 6,
        /// pak fragment využívající 1/10 kapacity bude vysoký 6 pixelů, a úměrně k tomu bude výška linky = 60 pixelů (namísto zadaných 30).
        /// Implicitní hodnota je 4 pixely.
        /// Platná hodnota je 2 až 25 pixelů. Jiné hodnoty budou zarovnány do těchto mezí.
        /// </summary>
        public int RowHeightMinFragmentPx { get { return (this._HasExplicitDeclaration && this._RowDeclaration.RowHeightMinFragmentPx > 0 ? this._RowDeclaration.RowHeightMinFragmentPx : this.Parent.RowHeightMinFragmentPx); } }
        /// <summary> { get; }
        /// Přídavek k výšce řádku, vyjádřený relativně k hodnotě this.LineHeight (fyzická výška jedné linky).
        /// Defaultní hodnota je 1F.
        /// Výška řádku se určí jako výška nejvyšší položky, zaokrouhlená na celé linky (LineHeight), a přičte se tento přídavek.
        /// </summary>
        internal decimal RowHeightAddLineSolidRatio { get { return (this._HasExplicitDeclaration && this._RowDeclaration.RowHeightAddLineSolidRatio > 0 ? this._RowDeclaration.RowHeightAddLineSolidRatio : this.Parent.RowHeightAddLineSolidRatio); } }
        /// <summary> { get; }
        /// Přídavek k výšce fragmentovaného řádku, vyjádřený relativně k hodnotě this.LineHeight (fyzická výška jedné linky).
        /// Defaultní hodnota je 1F.
        /// Výška řádku se určí jako výška nejvyšší položky, zaokrouhlená na celé linky (LineHeight), a přičte se tento přídavek.
        /// </summary>
        internal decimal RowHeightAddLineFragmentRatio { get { return (this._HasExplicitDeclaration && this._RowDeclaration.RowHeightAddLineFragmentRatio > 0 ? this._RowDeclaration.RowHeightAddLineFragmentRatio : this.Parent.RowHeightAddLineFragmentRatio); } }
        #endregion
		#region ELEMENTY : přidání, hledání, smazání, invalidace, detekce existence
		/// <summary>
		/// Přidá do sebe data elementů z dodaného seznamu.
		/// </summary>
		/// <param name="dataList">Seznam elementů</param>
		internal bool ChartElementsAdd(IEnumerable<IDataElement> dataList, bool allElementsLoaded)
		{
			bool isAdded = this.ChartElementsAdd(dataList);
			if (allElementsLoaded && !this.AllElementsLoaded)
				this.AllElementsLoaded = allElementsLoaded;
			return isAdded;
		}
		/// <summary>
		/// Přidá do sebe data elementů z dodaného seznamu.
		/// </summary>
		/// <param name="dataList">Seznam elementů</param>
		internal bool ChartElementsAdd(IEnumerable<IDataElement> dataList)
		{
			bool isAdded = false;
			lock (this._ElementDict)
			{
				foreach (IDataElement data in dataList)
				{
					if (!this._ElementDict.ContainsKey(data.GId))
					{
						GridDataChartElementCls element = new GridDataChartElementCls(this, data);
						if (element.IsConfiguredText)
							element.EvaluationDataSource = this.Parent.DataSource as IEvaluationDataSource;
						this._ElementDict.Add(data.GId, element);
						isAdded = true;
					}
				}
				if (isAdded)
					this._InvalidateAll();
			}
			return isAdded;
		}
		/// <summary>
		/// Přidá do sebe data daného elementu
		/// </summary>
		/// <param name="data">Element</param>
		internal bool ChartElementsAdd(IDataElement data)
		{
			bool isAdded = false;
			lock (this._ElementDict)
			{
				if (!this._ElementDict.ContainsKey(data.GId))
				{
					GridDataChartElementCls element = new GridDataChartElementCls(this, data);
					if (element.IsConfiguredText)
						element.EvaluationDataSource = this.Parent.DataSource as IEvaluationDataSource;
					this._ElementDict.Add(data.GId, element);
					isAdded = true;
				}
				if (isAdded)
					this._InvalidateAll();
			}
			return isAdded;
		}
		/// <summary>
		/// Přidá do sebe daný elementu.
		/// Používá se při přesunu elementu mezi dvěma řádky.
		/// </summary>
		/// <param name="elementData">Vizuální data elementu</param>
		internal bool ChartElementsAdd(GridDataChartElementCls elementData)
		{
			bool isAdded = false;
			lock (this._ElementDict)
			{
				if (!this._ElementDict.ContainsKey(elementData.GId))
				{
					elementData.SetParent(this);             // Vloží sebe jako parenta řádku
					this._ElementDict.Add(elementData.GId, elementData);
					isAdded = true;
				}
				if (isAdded)
					this._InvalidateAll();
			}
			return isAdded;
		}
		/// <summary>
		/// Invaliduje svoje vizuální data.
		/// V případě potřeby (get na Listy) si je vytvoří znovu, aktuální.
		/// </summary>
		private void _InvalidateAll()
		{
			this._ElementAllList = null;
			this._ElementVisibleList = null;
			this.RequestCalcVertical = true;
			this.RequestCalcHorizontal = true;
		}
		/// <summary>
		/// Informace o časovém rozsahu, pro který jsou načtena data v poli this.ChartElements.
		/// Při změně časového rozmezí se data načítají z podkladové datové vrstvy znovu.
		/// Hodnota Empty znamená, že data dosud nebyla načtena.
		/// </summary>
		internal TimeRange ChartElementLoadedTimeRange { get; set; }
		/// <summary>
		/// Najde a vrátí element pro daný GID, anebo vrátí null pokud neexistuje.
		/// Tato metoda zajistí, že element bude obsahovat korektní souřadnice v rámci svého řádku, 
		/// a to za každé situace (i těsně po načtení, i pro neviditelný řádek, i pro element mimo časovou osu).
		/// Tím je zajištěno, že volající funkce může získat jakýkoli element a spolehnout se na jeho souřadnici.
		/// Rovněž je v elementu nastaveno, zda se nachází ve viditelné části časové osy.
		/// Také je zohledněno, zda řádek sám je/není vybraný filtrem.
		/// Jedna věc zde není zohledněna: zda se řádek nachází (vertikálně) ve viditelné části grafu.
		/// Vertikální souřadnice uvedené v elementu se vztahují k řádku (Top = 0 = horní okraj řádku).
		/// Fyzická souřadnice Y je dána pozicí řádku (nodu).
		/// Poznámka: jeden datový řádek může být zobrazen ve více vizuálních řádcích (nody).
		/// </summary>
		/// <param name="graphGId">GID hledaného elementu</param>
		/// <returns>Data elementu</returns>
		internal GridDataChartElementCls GetChartElement(GID graphGId)
		{
			return this.GetChartElement(graphGId, false);
		}
		/// <summary>
		/// Najde a vrátí element pro daný GID, anebo vrátí null pokud neexistuje.
		/// Tato metoda zajistí, že element bude obsahovat korektní souřadnice v rámci svého řádku, 
		/// a to za každé situace (i těsně po načtení, i pro neviditelný řádek, i pro element mimo časovou osu).
		/// Tím je zajištěno, že volající funkce může získat jakýkoli element a spolehnout se na jeho souřadnici.
		/// Rovněž je v elementu nastaveno, zda se nachází ve viditelné části časové osy.
		/// Také je zohledněno, zda řádek sám je/není vybraný filtrem.
		/// Jedna věc zde není zohledněna: zda se řádek nachází (vertikálně) ve viditelné části grafu.
		/// Vertikální souřadnice uvedené v elementu se vztahují k řádku (Top = 0 = horní okraj řádku).
		/// Fyzická souřadnice Y je dána pozicí řádku (nodu).
		/// Poznámka: jeden datový řádek může být zobrazen ve více vizuálních řádcích (nody).
		/// </summary>
		/// <param name="graphGId">GID hledaného elementu</param>
		/// <param name="skipDimensionCheck">
		/// Požadavek, aby se neprováděly validace souřadnic nalezeného elementu.
		/// V některých situacích potřebujeme jen najít element (anebo jen získat informaci o tom, že tu je),
		/// a pak by určování souřadnic bylo zdržující.
		/// </param>
		/// <returns>Data elementu</returns>
		internal GridDataChartElementCls GetChartElement(GID graphGId, bool skipDimensionCheck)
		{
			GridDataChartElementCls element;

			// Vyhledání elementu:
			if (!this._ElementDict.TryGetValue(graphGId, out element))
				return null;

			if (!skipDimensionCheck)
			{
				// Validace souřadnic elementu:
				this._CurrentVisualItemListCheck();  // Zajistí platnost dat v seznamu this._CurrentVisualItemList, a platnosti dimenzí každého elementu.

				// Specifické doplnění dimenzí u elementu, pro který dosud nebyly interní dimenze určeny:
				element.InternalDimensionCheck();
			}

			return element;
		}
		/// <summary>
		/// Zajistí odebrání daného elementu.
		/// Používá se při editaci, kdy element odeberu z jednoho řádku a přidím jej do jiného.
		/// Současně zajistí invalidaci času, pro který jsou určena vizuální data (pravděpodobně bude třeba přepočítat vizuální data znovu).
		/// </summary>
		/// <param name="elementGId">GID elementu, který chcem vyjmout</param>
		internal bool ChartElementsRemove(GID elementGId)
		{
			bool result = false;
			lock (this._ElementDict)
			{	// Zámek nám zajistí, že po dobu modifikace záznamů v this.ChartElements nebude probíhat vizuální kalkulace návazných údajů:
				if (this._ElementDict.ContainsKey(elementGId))
				{
					this._ElementDict.Remove(elementGId);
					result = true;
				}
				if (result)
					this._InvalidateAll();           // Invaliduji pracovní data. V případě potřeby se vytvoří nově.
			}
			return result;
		}
		/// <summary>
		/// Zajistí přidání dočasného elementu podle požadavku na načítání do svého seznamu elementů.
		/// Dočasný element vizuálně reprezentuje časový úsek, pro který nyní probíhá načítání dat.
		/// Po dokončení reálného načtení dat bude tento dočasný element odebrán - viz metoda ChartElementsTempDel().
		/// </summary>
		/// <param name="request">Specifikace požadavku</param>
		internal void ChartElementsTempAdd(DataSourceRequestReadElements request)
		{
			GridDataElementCls element = GridDataElementCls.CreateTempElement(request);
			element.TextNote = "Data pro tento čas se načítají.";

			List<IDataElement> list = new List<IDataElement>();
			list.Add(element);
			this.ChartElementsAdd(list);
		}
		/// <summary>
		/// Zajistí odebrání dočasného elementu, vytvořeného podle požadavku na načítání do svého seznamu elementů.
		/// Současně zajistí invalidaci času, pro který jsou určena vizuální data (pravděpodobně bude třeba přepočítat vizuální data znovu).
		/// </summary>
		/// <param name="request"></param>
		internal bool ChartElementsTempDel(DataSourceRequest request)
		{
			bool result = false;
			GID tempGId = request.TempGId;
			lock (this._ElementDict)
			{	// Zámek nám zajistí, že po dobu modifikace záznamů v this.ChartElements nebude probíhat vizuální kalkulace návazných údajů:
				if (this._ElementDict.ContainsKey(tempGId))
				{
					this._ElementDict.Remove(tempGId);
					result = true;
				}
				if (result)
					this._InvalidateAll();           // Invaliduji pracovní data. V případě potřeby se vytvoří nově.
			}
			return result;
		}
		/// <summary>
		/// Smaže všechna data grafů z paměti, plus příslušné registry
		/// </summary>
		internal void ChartElementClear()
		{
			this._ChartElementClear(null);
		}
		/// <summary>
		/// Smaže všechna data grafů z paměti, plus příslušné registry.
		/// Ponechá v paměti ty elementy, které jsou uvedeny v daném seznamu rezerovovaných elementů.
		/// </summary>
		internal void ChartElementClear(IEnumerable<GID> reservedElements)
		{
			this._ChartElementClear(reservedElements);
		}
		/// <summary>
		/// Smaže všechna data grafů z paměti, plus příslušné registry.
		/// Ponechá v paměti ty elementy, které jsou uvedeny v daném seznamu rezerovovaných elementů.
		/// </summary>
		private void _ChartElementClear(IEnumerable<GID> reservedElements)
		{
			if (this._ElementDict == null)
				this._ElementDict = new Dictionary<GID, GridDataChartElementCls>();
			
			lock (this._ElementDict)
			{	// Zámek nám zajistí, že po dobu modifikace záznamů v this.ChartElements nebude probíhat vizuální kalkulace návazných údajů:
				Dictionary<GID, GridDataChartElementCls> reserved = new Dictionary<GID, GridDataChartElementCls>();
				if (reservedElements != null)
				{	// Sestavím index elementů, které chci zachránit:
					foreach (GID gId in reservedElements)
					{
						GridDataChartElementCls element;
						if (!reserved.ContainsKey(gId) && this._ElementDict.TryGetValue(gId, out element))
							reserved.Add(gId, element);
					}
				}
				// Clear(), nikoli   new Dictionary<GID, GridDataChartElementCls>();
				//  protože na objektu je lock !
				this._ElementDict.Clear();
				if (reserved.Count > 0)
				{	// Vrátím tam elementy, které se nemají ztratit:
					foreach (KeyValuePair<GID, GridDataChartElementCls> kvp in reserved)
						this._ElementDict.Add(kvp.Key, kvp.Value);
				}

				this.ChartElementLoadedTimeRange = TimeRange.Empty;
				this._AllElementsLoaded = false;
				this._InvalidateAll();
			}
		}
		/// <summary>
		/// Příznak, že elementy načtené do pole this.ElementList představují úplnou sadu elementů, 
		/// a pro tento řádek (this) nebude nikdy zapotřebí donačítat elementy pro širší časové pole.
		/// </summary>
		internal bool AllElementsLoaded
		{
			get { return _AllElementsLoaded; }
			set
			{
				if (_AllElementsLoaded && !value)
					Throw.SysError(MessageInfo.Get("Není možno zrušit příznak «AllElementsLoaded» z hodnoty true na false."));
				_AllElementsLoaded = value;
			}
		}
		private bool _AllElementsLoaded;
		/// <summary>
		/// Zajistí, že budou připravena data všech elementů, a přepočtena výška řádku a všech linek.
		/// Nezajistí přípravu viditelných elementů.
		/// </summary>
		internal void AllListCheck()
		{
			this._ElementAllListCheck();
		}
		/// <summary>
		/// Vypočte a vrátí souhrnný časový interval všech (dosud načtených) elementů.
		/// </summary>
		internal TimeRange AllElementsTimeRange
		{
			get
			{
				TimeRange timeRange = TimeRange.Empty;
				foreach (GridDataChartElementCls element in this._ElementDict.Values)
					timeRange = timeRange + element.TimeRange;
				return timeRange;
			}
		}
		/// <summary>
		/// Obsahuje true, pokud v tomto řádku existuje nějaký element.
		/// </summary>
		internal bool ContainAnyElement { get { return (this._ElementDict.Count > 0); } }
		/// <summary>
		/// Vrátí true, pokud v tomto řádku existuje konkrétní element.
		/// Velice rychlá metoda. Neprovádí žádné donačítání a ani dokalkulace souřadnic elementu.
		/// Jen jednoduchý dotaz do interní dictionary.
		/// </summary>
		/// <param name="gID">GID elementu, na který se ptáme</param>
		/// <returns></returns>
		internal bool ContainElement(GID gID)
		{
			return this._ElementDict.ContainsKey(gID);
		}
		#endregion
		#region PRIVÁTNÍ SPRÁVA DAT : zpracování vstupních položek do interních seznamů, přepočty, rozmístění, výpočty Rectangle
		/// <summary>
		/// Metoda zajistí, že bude připraven seznam všech elementů, s přepočítanými souřadnicemi Top, Bottom (relativní).
		/// Pokud již nyní existuje, a není nutno jej připravit znovu, přípravu nedělá a okamžitě skončí.
		/// </summary>
		private void _ElementAllListCheck()
		{
			if (this._ElementAllListNeedPrepare)
				this._ElementAllListPrepare();
		}
		/// <summary>
		/// Z dat ve slovníku s datovými prvky připraví vizuální seznam.
		/// Zamkne data (this.DataItemDict) a volá výkonnou metodu this._VisualItemListPrepareLock()
		/// </summary>
		private void _ElementAllListPrepare()
		{
			lock (this._ElementDict)
			{
				this._ElementAllListPrepareLock();
			}
		}
		/// <summary>
		/// Z dat ve slovníku s datovými prvky připraví vizuální seznam.
		/// Očekává se, že data jsou po tuto dobu zamčená.
		/// </summary>
		private void _ElementAllListPrepareLock()
		{
			this._ElementAllList = new List<GridDataChartElementCls>();

			List<GridDataChartElementCls> dataList = new List<GridDataChartElementCls>(this._ElementDict.Values);
			// Setřídit všechny elementy podle času počátku
			dataList.Sort(GridDataChartElementCls.CompareByBegin);

			decimal sumMax = 0M;
			decimal oneMin = 0M;
			bool containFragments = false;

			// Pracovní seznam s informacemi o nyní přítomných položkách ve vizuálním seznamu, v okolí aktuálního času (tj. data.Begin):
			// Obsahuje položky = předchozí elementy (předchozí dle času jejich Begin), a které trvají přinejmenším v čase Begin tohoto elementu.
			// Popisuje jejich vertikální pozici v grafu, v relativních hodnotách (kde 1 = jedna výrobní linka = stroj),
			// a pomáhá tak určovat relativní pozici pro nově umisťovaný element = tak, aby se vešel mezi stávající elementy, anebo se umístil nad ně.
			// Skládá tak elementy na sebe jako v Tetrisu, s tím že element může propadnout i do nižšího "řádku", pokud se tam celý vejde
			//  (což je prioritní postup).
			List<_UseItem> useList = new List<_UseItem>();

			// Pro každý element (bez ohledu na jejich viditelnost v aktuální časové ose):
			foreach (GridDataChartElementCls data in dataList)
			{
				// V tuto chvíli není žádný element viditelný (na konci této metody se do this._CurrentVisualItemList zapisuje null):
				// Viditelnost elementů se řeší v metodě this._CurrentVisualItemListPrepareLock(), tam se budou setovat viditelné elementy.
				data.SetVisibleAxis(false);

				bool addVisualItem = false;
				decimal relativeBottom = 0M;
				decimal relativeTop = 1M;
				decimal relativeHeight = 1M;

				if (data.IsTemporary)
				{
					addVisualItem = true;
				}
				else if (data.IsSubLayer)
				{
					addVisualItem = true;
				}
				else if (data.IsItemLayer && this.RowItem.RowGraph == RowGraphMode.TaskCapacityLink)
				{	// Element typu TaskCapacityLink: zde se používá dělení podle UseRatio, třídím položky nad sebe jako v Tetrisu:
					// Klíčový okamžik je Begin tohoto elementu:
					DateTime begin = data.TimeRange.Begin;

					// Z pracovního seznamu vyhodím všechny prvky, které mají konec v nebo před datem begin:
					useList.RemoveAll(u => u.Time.End <= begin);
					// Nyní mám v seznamu useList jen ty prvky, které začaly před naším prvkem (pořadí zpracování je Begin ASC), 
					//   a které končí za naším prvkem.
					// Tedy jde o konfliktní prvky.

					// Zde najdu (odspodu) takový element v seznamu useList (prohledávám jej odspodu), 
					//  pod který by se na výšku vešel náš element:
					relativeHeight = (decimal)data.UseRatio; // Potřebný prostor pro náš element
					int insertAt = -1;                       // Index prvku v seznamu useList, před který vložím náš element.
					int useCount = useList.Count;
					if (useCount > 0)
					{	// Pokud máme nějaký (alespoň jeden) prvek, musím vyřešit, kam by bylo možno vložit (insertovat) aktuální prvek v tomto seznamu = na kterou pozici Bottom:
						decimal lastTop = 0M;
						for (int u = 0; u < useCount; u++)
						{
							if ((useList[u].Bottom - lastTop) >= relativeHeight)
							{	// Pokud pod tímto prvkem (jeho Bottom) je dolů směrem k lastTop tolik místa, aby se tam vešel aktuální prvek:
								insertAt = u;                        // Index, na který vložíme do pracovního seznamu useList záznam
								relativeBottom = lastTop;
								break;
							}
							// Pamatuji si: počátek volného prostoru (bottom pro příští prvek) 
							//  by mohl být na vrcholku současného existujícího prvku:
							lastTop = useList[u].Top;
						}
						// Pokud jsem nenašel žádné volné místo MEZI prvky, pak nový prvek umístím NAD poslední prvek:
						if (insertAt < 0)
							relativeBottom = lastTop;
					}

					// Pracovní seznam: do něj vložíme/přidáme záznam za tento datový prvek:
					relativeTop = relativeBottom + relativeHeight;
					_UseItem useItem = new _UseItem(data.TimeRange, relativeBottom, relativeTop);
					if (insertAt < 0)
						useList.Add(useItem);
					else
						useList.Insert(insertAt, useItem);

					// Střádám hodnoty (sumMax a oneMin):
					if (useItem.Top > sumMax) sumMax = useItem.Top;
					if (relativeHeight > 0M && (oneMin == 0M || relativeHeight < oneMin)) oneMin = relativeHeight;
					if (!containFragments && relativeHeight != 1) containFragments = true;      // obsahuje zlomky?

					// Vizuální prvek bude přidán do výsledného seznamu:
					addVisualItem = true;
				}
				else
				{	// Ostatní elementy:
					addVisualItem = true;
				}

				// Prvek se má vložit do soupisu _VisualItemList:
				if (addVisualItem)
				{
					data.StoreRelativeY(relativeBottom, relativeTop);
					this._ElementAllList.Add(data);
				}
			}

			// Zapamatuji si mezní hodnoty a zajistím přepočet výšky řádku:
			this.UseRatioSumMax = sumMax;
			this.UseRatioOneMin = oneMin;
			this.ContainFragments = containFragments;

			this._RowHeightRecalc();

			// Setřídím výsledné položky podle vrstvy a času (SubLayer a TempLayer půjdou dospodu):
			this._ElementAllList.Sort(GridDataChartElementCls.CompareByLayerBegin);

			// Shodím explicitní příznak, který vede k mému spouštění:
			this.RequestCalcVertical = false;

			// Zajistím invalidaci current vizuálního seznamu (tím zajistím nutnost jeho znovuvytvoření):
			this._ElementVisibleList = null;
		}
		/// <summary>
		/// Metoda zajistí, že bude připraven seznam souřadnic linek.
		/// Pokud již nyní existuje, a není nutno jej připravit znovu, přípravu nedělá a okamžitě skončí.
		/// </summary>
		private void _UnitLineListCheck()
		{
			if (this._UnitLineListNeedPrepare)
				this._UnitLineListPrepare();
		}
		/// <summary>
		/// Připraví data do seznamu this._CurrentLineList.
		/// Zamkne data (this.DataItemDict) a volá výkonnou metodu this._CurrentLineListPrepareLock()
		/// </summary>
		private void _UnitLineListPrepare()
		{
			lock (this._ElementDict)
			{
				this._UnitLineListPrepareLock();
			}
		}
		/// <summary>
		/// Připraví data do seznamu this._CurrentLineList.
		/// Očekává se, že data jsou po tuto dobu zamčená.
		/// </summary>
		private void _UnitLineListPrepareLock()
		{
			// Pokud někdo chce získat LineList, ale dosud neexistuje VisualList a tím pádem ani korektní výpočet výšek 
			//   (neproběhla tedy metoda _RowHeightRecalc()), je třeba vygenerovat this._VisualItemList a přepočítat výšky:
			if (this._ElementAllListNeedPrepare)
				this._ElementAllListPrepareLock();

			this._UnitLineList = new List<int>();

			int line = 0;
			decimal max = this.LineHeightCurrRatio;
			while (((decimal)line) <= max)
				this._UnitLineList.Add(this._DimYRecalc(line++) - 1);

			// Uložíme si vizuální dimenze, pro které jsme seznam this._CurrentVisualItemList přepočítali (tím zabráním opakovanému spouštění:
			this._CalcLineListRowHeight = this.RowHeightPx;
			this._CalcLineListLineHeight = this.LineHeightCurrPx;
			this._CalcLineHeightCurrRatio = this.LineHeightCurrRatio;
		}
		/// <summary>
		/// Metoda zajistí, že bude připraven seznam viditelných elementů.
		/// Pokud již nyní existuje, a není nutno jej připravit znovu, přípravu nedělá a okamžitě skončí.
		/// </summary>
		private void _CurrentVisualItemListCheck()
		{
			if (this._ElementVisibleListNeedPrepare)
				this._CurrentVisualItemListPrepare();
		}
		/// <summary>
		/// Připraví data do seznamu this._CurrentVisualItemList.
		/// Seznam bude obsahovat ty prvky seznamu this.VisualItemList, které spadají do oblasti viditelného časového intervalu this.VisibleTimeRange.
		/// Zamkne data (this.DataItemDict) a volá výkonnou metodu this._CurrentVisualItemListPrepareLock()
		/// </summary>
		private void _CurrentVisualItemListPrepare()
		{
			lock (this._ElementDict)
			{
				this._CurrentVisualItemListPrepareLock();
			}
		}
		/// <summary>
		/// Připraví data do seznamu this._CurrentVisualItemList.
		/// Seznam bude obsahovat ty prvky seznamu this.VisualItemList, které spadají do oblasti viditelného časového intervalu this.VisibleTimeRange.
		/// Očekává se, že data jsou po tuto dobu zamčená.
		/// </summary>
		private void _CurrentVisualItemListPrepareLock()
		{
			if (this.TimeAxisCalc != null)
			{
				// Pořadí nelze měnit: při získávání seznamu this.VisualItemList může dojít k tomu, že se do this._CurrentVisualItemList vloží null.
				List<GridDataChartElementCls> visualItemList = this.ElementAllList;       // Zde může proběhnout příprava dat do seznamu this.VisualItemList (viz get; )
				this._ElementVisibleList = new List<GridDataChartElementCls>();        // V rámci přípravy seznamu VisualItemList mohl výt vynulován objekt this._CurrentVisualItemList.

				int rowHeight = this.RowHeightPx;
				int lineHeight = this.LineHeightCurrPx;
				TimeRange visibleTimeRange = this.TimeAxisCalc.TimeRange;
				foreach (GridDataChartElementCls element in visualItemList)
				{
					//  Dimenze se vkládají i do neviditelných elementů:
					OneDimensionI dimX = this._DimXRecalc(element);             // Souřadnice X jsou odvozeny od času elementu a od časové osy
					OneDimensionI dimY = this._DimYRecRow(element);             // Souřadnice Y přes celý řádek
					element.StoreElementRowArea(dimX, dimY);                    // Do ElementRowArea se uloží pozice přes celý řádek
					dimY = this._DimYRecRel(element);                           // Souřadnice Y podle relativních souřadnic
					element.StoreElementRelArea(dimX, dimY);                    // Do ElementRelArea se uloží relativní pozice

                    if (element.TimeRange.Seconds <= 0d)
                    { }

                    if (element.TimeRange.IsIn(visibleTimeRange) && element.AddToVisibleByWidth())
					{
						this._ElementVisibleList.Add(element);
						// Nastavím viditelnost elementu na true:
						// (Na hodnotu false bylo nastaveno v metodě this._VisualItemListPrepareLock())
						// element.SetVisibleAxis(true);
						// element.InternalDimensionCalculate();                   // Automaticky doplním interní dimenze
					}
				}

				// Uložíme si vizuální dimenze, pro které jsme seznam this._CurrentVisualItemList přepočítali (tím zabráním opakovanému spouštění:
				this._CalculatedTimeAxisState = this.TimeAxisCalc.CurrentState;  // Jde o struct, uloží se kopie údaje vráceného z časové osy
				this._CalculatedLineHeight = this.LineHeightCurrPx;
				this._CalculatedRowHeight = this.RowHeightPx;

                // Elementy ve vizuálních seznamech setřídím dle času a umožním provést úpravy jejich dimenzí, pokud jsou elementy příliš úzké a mají okolo sebe nějaký prostor:
                this._CurrentVisualItemListPrepareFinal(this._ElementVisibleList);

                // Shodím explicitní příznak, který vede k mému spouštění:
				this.RequestCalcHorizontal = false;
			}
			else
			{
				this._ElementVisibleList = new List<GridDataChartElementCls>();
			}
		}
        /// <summary>
        /// V daném seznamu vizuálních elementů provede finální přípravu.
        /// Elementy jsou vybrány jako vhodné k zobrazení. Mají korektně určeny souřadnice podle časové osy.
        /// V průběhu této metody se seznam setřídí dle souřadnice Top a pak dle času počátku (souřadnice Left).
        /// Poté pro elementy se šířkou 3 pixely a menší zjistí, zda by je bylo možno rozšířit (podle místa, které okolo sebe mají).
        /// Následně určí jednak rozšíření, a jednak možnost modifikace vnitřní kresby pro elementy, jejichž finální šířka je 3 px a menší.
        /// Elementy se šířkou 0 px nastaví jako neviditelné.
        /// </summary>
        /// <param name="elementList"></param>
        private void _CurrentVisualItemListPrepareFinal(List<GridDataChartElementCls> elementList)
        {
            // Setřídíme podle času:
            elementList.Sort(GridDataChartElementCls.CompareByLayerBegin);

            // Nyní projdu elementy, a pokud některý má šířku menší než 3 pixely, zkusím nad ním pouvažovat:
            int c = elementList.Count;
            int l = c - 1;
            for (int e = 0; e < c; e++)
            {
                GridDataChartElementCls elementCurr = elementList[e];
                if (elementCurr.NeedAddVisualWidth)
                {   // Máme element, který je úzký (0,1,2 pixely), ale jeho čas je nula nebo kladný => měl by být vidět:
                    // Najdu element vlevo a vpravo, který je v naší vizuální vodorovné lince (ignoruji elementy, s nimiž se nepotkávám na souřadnicích Top, Bottom):
                    GridDataChartElementCls elementPrev = _FindNearestElement(elementList, e, -1);
                    GridDataChartElementCls elementNext = _FindNearestElement(elementList, e, +1);

                    // Funkce pro "široké lokty" u malého elementu je zde:
                    // Zajistí to, že pokud element vlevo a/nebo vpravo je buď dostatečně daleko, anebo může ustoupit, udělá to a uvolní kousek místa pro náš element.
                    // Pokud se to podaří, bude náš element o kousek širší a bude viditelný. Pokud mu zůstane šířka 0px, pak se nebude zobrazovat (a nebude zatěžovat vizuální vrstvu).
                    _CurrentVisualItemWideElement(elementCurr, elementPrev, elementNext);
                }
                else
                {
                    elementCurr.SetVisibleAxis(true);
                }
                if (elementCurr.IsInVisibleAxis)
                    elementCurr.InternalDimensionCalculate();         // Automaticky doplním interní dimenze  
            }

            // Odeberu elementy, které nejsou viditelné:
            elementList.RemoveAll(el => !el.IsInVisibleAxis);
        }
        /// <summary>
        /// Najde a vrátí sousední element v daném seznamu, který je na stejné lince Y a na stejné vrstvě DataElement.ElementLayer jako je elementCurrent.
        /// Hledá doleva (step = -1) nebo doprava (step = +1).
        /// </summary>
        /// <param name="elementList"></param>
        /// <param name="index"></param>
        /// <param name="step"></param>
        /// <param name="elementCurrent"></param>
        /// <returns></returns>
        private GridDataChartElementCls _FindNearestElement(List<GridDataChartElementCls> elementList, int index, int step)
        {
            int count = elementList.Count;
            GridDataChartElementCls elementCurrent = elementList[index];
            int ect = elementCurrent.ElementRelArea.Top;
            int ecb = elementCurrent.ElementRelArea.Bottom;
            GraphElementLayerType ecl = elementCurrent.DataElement.ElementLayer;
            while (true)
            {
                index += step;
                if (index < 0 || index >= count) break;
                GridDataChartElementCls elementTest = elementList[index];
                if (elementTest.DataElement.ElementLayer != ecl) continue;
                if (elementTest.ElementRelArea.Top >= ecb || elementTest.ElementRelArea.Bottom <= ect) continue;
                return elementTest;
            }
            return null;
        }
        /// <summary>
        /// Pokusí se rozšířit aktuální element tak, aby byl vidět.
        /// Na vstupu je element se šířkou 0,1,2 pixely.
        /// Element vlevo a/nebo vpravo může být null (pak v daném směru nic nebrání rozšíření).
        /// Sousední elementy mohou být těsně vedle, nebo může být mezi nimi mezera.
        /// Aktuální element rozšiřuje nejvíce o 1 pixel na každou stranu, přičemž element s výchozí šířkou 2 pixely rozšiřuje jen na jednu stranu = 3 pixely stačí.
        /// Pokud je sousední element na těsno vedle, pak se může uhnout jen tehdy, pokud jeho šířka po zmenšení bude 3 pixely a více.
        /// Přičemž platí, že sousední element vpravo může uhnout pouze do šířky 4 pixely, protože může být požádán o zmenšení i z druhé strany 
        /// (tj. když se ocitne vlevo), a pak uhne o další pixel na 3 pixely.
        /// Pokud aktuální element bude mít po rozšíření stále šířku 0, nebude nastaven jako viditelný.
        /// </summary>
        /// <param name="elCurr">Element, který by se měl rozšířit</param>
        /// <param name="elPrev">Element, který stojí vlevo vedle něj</param>
        /// <param name="elNext">Element, který stojí vpravo vedle něj</param>
        private void _CurrentVisualItemWideElement(GridDataChartElementCls elCurr, GridDataChartElementCls elPrev, GridDataChartElementCls elNext)
        {
            // Poznámka k souřadnicím (pro úplné grafické analfabety):
            // Left je pixel, kde začíná kresba
            // Width je počet obarvených pixelů (čtverečky na papíře)
            // Right je pixel, kde už kresba není !!!   To je souřadnice prvního pixelu vpravo od posledního obarveného pixelu.
            //     Pokud další element začíná (má Left) na souřadnici Right předešlého elementu, pak na sebe těsně navazují, ale nepřekážejí si.
            // Má-li element vlevo uhnout a udělat nám místo, pak jde o následující příklad:
            //  Prev.Left  = 20
            //  Prev.Width =  5
            //  Prev.Right = 25
            //  Curr.Left  = 25
            //  Curr.Width =  0
            // Element vlevo má svůj pravý okraj (poslední obarvený pixel) na pixelu 24.
            // Náš element by rád začínal na pixelu 25, má šířku 0. (Zatím neřeším, co je vpravo vedle něj.)
            // Element vlevo je dost široký, může ustoupit o 1 pixel, pak budou souřadnice:
            //  Prev : Left=20, Width=4, Right=24, poslední obarvený pixel = (Right-1)=23
            //  Curr : Left=24, Width=1, Right=25, jediný obarvený pixel = 24

            // Souřadnice obarveného pixelu vlevo a vpravo:
            int prevLast = (elPrev == null ? elCurr.ElementRelArea.Left - 10 : elPrev.ElementRelArea.Right - 1);   // Poslední pixel elementu Prev (anebo null => dostatečný prostor 10 px)
            int nextFirst = (elNext == null ? elCurr.ElementRelArea.Right + 10 : elNext.ElementRelArea.Left);      // První pixel elementu Next (anebo null => dostatečný prostor 10 px)

            // Doleva:
            if (elCurr.ElementRelArea.Width <= 2)
            {
                bool wl = false;
                if (prevLast < (elCurr.ElementRelArea.Left - 1))
                {   // Vlevo nic není, anebo je a poslední obarvený pixel (prevLast) je dostatečně daleko vlevo ( -1 ), abych mohl Current rozšířit doleva (o ten 1 pixel):
                    wl = true;
                }
                else if (prevLast == (elCurr.ElementRelArea.Left - 1) && elPrev.ElementRelArea.Width >= 4)
                {   // Vlevo je element, který má Right == naše Left (náš by chtěl začínat na jeho posledním pixelu), a ten vlevo je dostatečně široký:
                    elPrev.ElementRelArea.Width--;
                    prevLast--;
                    wl = true;
                }
                if (wl)
                {
                    elCurr.ElementRelArea.X--;
                    elCurr.ElementRelArea.Width++;
                }
            }

            // Doprava:
            if (elCurr.ElementRelArea.Width <= 2)
            {
                bool wr = false;
                if (nextFirst > elCurr.ElementRelArea.Right)
                {   // Vpravo nic není, anebo je a první obarvený pixel (nextFirst) je dostatečně daleko vpravo za naší souřadnicí Right (to je pozice prvního pixelu, který element curr neobarvuje):
                    wr = true;
                }
                else if (nextFirst == elCurr.ElementRelArea.Right && elNext.ElementRelArea.Width >= 5)
                {   // Vpravo je element, který má Left == naše Right (náš by se rád posunul doprava na jeho pixel Left), a ten vpravo je dostatečně široký:
                    elNext.ElementRelArea.X++;
                    elNext.ElementRelArea.Width--;
                    nextFirst++;
                    wr = true;
                }
                if (wr)
                {
                    elCurr.ElementRelArea.Width++;
                }
            }

            // Viditelný element je jen takový, který má kladnou šířku (tady neřeším nulové viditelné elementy, ale jen elementy, které jsou vidět díky svému tvaru)
            elCurr.SetVisibleAxis(elCurr.ElementRelArea.Width > 0);
        }
		/// <summary>
		/// Metoda přepočte podle aktuálních dat výšku řádku a uloží ji do proměnné this.RowHeightPx
		/// </summary>
		private void _RowHeightRecalc()
		{
			// Výška řádku v ratio čísle (0-1) = this.UseRatioSumMax zaokrouhlené nahoru na celé číslo, nejméně 1, s přičtením AddLinesRatio:
			bool containUseRatio = this.ContainUseRatio;             // true = tento řádek obsahuje fragmenty s UseRatio < 1
			decimal useRatioSumMax = (this.UseRatioSumMax < 1M ? 1M : this.UseRatioSumMax);
			decimal addLinesRatio = (!containUseRatio ? this.RowHeightAddLineSolidRatio : this.RowHeightAddLineFragmentRatio); // Přídavek k řádku v poměru k výšce linky (0-1)
			decimal lineHeightPx = (!containUseRatio ? this.RowHeightLineSolidPx : this.RowHeightLineFragmentPx);  // Výška linky v pixelech
			decimal relativeRowHeight = Math.Ceiling(useRatioSumMax) + addLinesRatio;             // Výška řádku relativní

			// Kontrola nejmenšího fragmentu, určení výšky linky:
			decimal minRatio = this.UseRatioOneMin;
			if (minRatio > 0M && minRatio < 1M)
			{
				decimal lineHeightDc = (decimal)lineHeightPx;
				decimal minFragmentDc = (decimal)this.RowHeightMinFragmentPx;
				decimal minFragmentHeight = minRatio * lineHeightDc; // Jak by byl vysoký nejmenší fragment
				if (minFragmentHeight < minFragmentDc)
				{	// Zvětším výšku linky tak, aby nejmenší fragment byl vysoký this.RowHeightMinFragmentPx:
					lineHeightPx = (int)(Math.Ceiling(minFragmentDc / minRatio));
				}
			}
			lineHeightPx = (lineHeightPx < 7M ? 7M : (lineHeightPx > 100M ? 100M : lineHeightPx));   // Do rozmezí 7 - 100, včetně

			// Výška řádku v pixelech, vypočtená bez korekce:
			decimal rowHeightPx = relativeRowHeight * lineHeightPx;

			// Korekce na minimální a maximální výšku řádku v pixelech:
            if (this.RowHeightTotalMinPx > 10 && rowHeightPx < this.RowHeightTotalMinPx)
            {
                rowHeightPx = this.RowHeightTotalMinPx;
                // Tady nenavyšujeme výšku linky, tu necháme jak byla vypočtena.
            }
			if (this.RowHeightTotalMaxPx > 10 && rowHeightPx > this.RowHeightTotalMaxPx) // Máme horní limit pro výšku, a překročili jsme ho?
			{	// Výška řádku bude == maximum, a přepočteme koeficient výšky jedné linky:
				rowHeightPx = this.RowHeightTotalMaxPx;
				lineHeightPx = rowHeightPx / relativeRowHeight;
			}

			// Uložíme výsledky:
			this._RowHeightPx = ((int)(Math.Round(rowHeightPx, 0))) + 2;
			this.LineHeightCurrPx = (int)(Math.Round(lineHeightPx, 0));
			this.LineHeightCurrRatio = relativeRowHeight;

			// Zajistíme vyvolání přepočtu _CurrentLineList:
			this._UnitLineList = null;
		}
		/// <summary>
		/// Provede přepočet souřadnic ve směru X pro danou položku.
		/// </summary>
		private OneDimensionI _DimXRecalc(GridDataChartElementCls item)
		{
			if (this.TimeAxisCalc == null) return OneDimensionI.Empty;
			int left = this.TimeAxisCalc.GetPixelForTime(item.TimeRange.Begin);
			int right = this.TimeAxisCalc.GetPixelForTime(item.TimeRange.End);
			return new OneDimensionI(left, right - left);
		}
		/// <summary>
		/// Provede přepočet souřadnic ve směru Y pro danou položku.
		/// Vypočte souřadnice Y pro výšku elementu přes celý řádek: počínaje Top = 1, Height = RowHeightPx - 2.
		/// Naproti tomu metoda this._DimYRecRel() určí pozici Y podle relativních souřadnic RelativeTop, RelativeBottom.
		/// </summary>
		/// <param name="item"></param>
		/// <returns></returns>
		private OneDimensionI _DimYRecRow(GridDataChartElementCls item)
		{
			int rowHeight = this._RowHeightPx - 2;
			int lineHeight = rowHeight;

			int top = (int)Math.Round((decimal)lineHeight * item.RelativeTop, 0);          // Top měřeno odspodu nahoru (opačně než WinForm)
			int bottom = (int)Math.Round((decimal)lineHeight * item.RelativeBottom, 0);    // Bottom, stejně tak
			OneDimensionI dimY = new OneDimensionI(rowHeight - top + 1, top - bottom);     // Převést na souřadnice WinForm, se základnou rowHeight
			return dimY;
		}
		/// <summary>
		/// Provede přepočet souřadnic ve směru Y pro danou položku.
		/// Vypočte souřadnice Y pro výšku elementu podle relativních souřadnic RelativeTop, RelativeBottom.
		/// Naproti tomu metoda this._DimYRecRow() určí pozici Y přes celý řádek: počínaje Top = 1, Height = RowHeightPx - 2.
		/// </summary>
		/// <param name="item"></param>
		/// <returns></returns>
		private OneDimensionI _DimYRecRel(GridDataChartElementCls item)
		{
			int rowHeight = this.RowHeightPx - 2;
			int lineHeight = this.LineHeightCurrPx;

			int top = (int)Math.Round((decimal)lineHeight * item.RelativeTop, 0);          // Top měřeno odspodu nahoru (opačně než WinForm)
			int bottom = (int)Math.Round((decimal)lineHeight * item.RelativeBottom, 0);    // Bottom, stejně tak
			OneDimensionI dimY = new OneDimensionI(rowHeight - top + 1, top - bottom);     // Převést na souřadnice WinForm, se základnou rowHeight
			return dimY;
		}
		/// <summary>
		/// Provede přepočet souřadnic ve směru Y pro danou položku.
		/// Na vstupu je relativní pozice, počínaje 0 = dole, 1 = jeden logický řádek.
		/// Na výstupu je absolutní pozice v pixelech, 0 = nahoře.
		/// </summary>
		/// <param name="item"></param>
		/// <returns></returns>
		private int _DimYRecalc(decimal ratio)
		{
			int rowHeight = this.RowHeightPx;
			int lineHeight = this.LineHeightCurrPx;

			int y = (int)Math.Round((decimal)lineHeight * ratio, 0);
			return rowHeight - y;
		}
		#region HLÍDÁNÍ ZMĚNY VIZUÁLNÍHO PROSTORU PRO DATA (TimeRange, Width, RowHeight, LineHeight) - paměť hodnot, pro které jsou provedeny přepočty. Detekce změny.
		/// <summary>
		/// Aktuální stav časové osy (časový interval a jeho vizuální šířka).
		/// Stav lze porovnat (==, !=) a lze jej ukládat do privátní proměnné, jde o strukturu takže se uloží kopie.
		/// Pro tento čas je nyní přepočtem seznam ElementVisibleList.
		/// </summary>
		private TimeRangeWidth _CalculatedTimeAxisState;
		/// <summary> { get; }
		/// Aktuální hodnota výšky viditelného řádku, celý řádek, v pixelech.
		/// Pro tuto výšku je nyní přepočten seznam ElementVisibleList.
		/// </summary>
		private int _CalculatedRowHeight { get; set; }
		/// <summary> { get; }
		/// Aktuální hodnota výšky jedné linky (ekvivalent hodnoty UseRatio = 1), v pixelech.
		/// Pro tuto výšku je nyní přepočten seznam ElementVisibleList.
		/// </summary>
		private int _CalculatedLineHeight { get; set; }
		/// <summary> { get; }
		/// Aktuální hodnota výšky viditelného řádku, celý řádek, v pixelech.
		/// Pro tuto výšku je nyní přepočten seznam UnitLineList.
		/// </summary>
		private int _CalcLineListRowHeight { get; set; }
		/// <summary> { get; }
		/// Aktuální hodnota výšky jedné linky (ekvivalent hodnoty UseRatio = 1), v pixelech.
		/// Pro tuto výšku je nyní přepočten seznam UnitLineList.
		/// </summary>
		private int _CalcLineListLineHeight { get; set; }
		/// <summary>
		/// Aktuální hodnota počtu linek (Ratio), ne pixely.
		/// Pro tento čas je nyní přepočten seznam UnitLineList.
		/// </summary>
		private decimal _CalcLineHeightCurrRatio { get; set; }
		/// <summary>
		/// Vrací příznak, zda je třeba zavolat metodu pro přípravu dat do this._VisualItemList (trvalý seznam dat).
		/// </summary>
		/// <returns></returns>
		private bool _ElementAllListNeedPrepare
		{
			get
			{
				if (this._ElementAllList == null) return true;
				if (this.RequestCalcVertical) return true;
				return false;
			}
		}
		/// <summary>
		/// Vrací příznak, zda je třeba zavolat metodu pro přípravu dat do this._CurrentLineList (seznam pozic jednotlivých Lines).
		/// </summary>
		/// <returns></returns>
		private bool _UnitLineListNeedPrepare
		{
			get
			{
				if (this._UnitLineList == null) return true;

				if (this._CalcLineListRowHeight != this.RowHeightPx) return true;            // Změna výšky řádku
				if (this._CalcLineListLineHeight != this.LineHeightCurrPx) return true;      // Změna výšky linky
				if (this._CalcLineHeightCurrRatio != this.LineHeightCurrRatio) return true;  // Změna počtu linek

				return false;
			}
		}
		/// <summary>
		/// Vrací příznak, zda je třeba zavolat metodu pro přepočet aktuálních dat do this._CurrentVisualItemList (nyní viditelný seznam dat).
		/// </summary>
		/// <returns></returns>
		private bool _ElementVisibleListNeedPrepare
		{
			get
			{
				if (this._ElementVisibleList == null) return true;
				if (this.RequestCalcHorizontal) return true;

				// Pokud není připravena časová osa, vrátím false. 
				// Pokud je připravena, a má jiné rozměry než pro které jsme posledně kalkulovali naše data, vrátím true = příprava se provede.
				if (this.TimeAxisCalc == null) return false;

				// Změna kalkulovaných dat proti datům aktuální osy:
				if (this._CalculatedTimeAxisState != this.TimeAxisCalc.CurrentState) return true;  // Změna časové osy
				if (this._CalculatedLineHeight != this.LineHeightCurrPx) return true;      // Změna výšky linky
				if (this._CalculatedRowHeight != this.RowHeightPx) return true;            // Změna výšky řádku

				return false;
			}
		}
		#endregion
		#endregion
		#region FIND ELEMENT BY LOCATION
		/// <summary>
		/// Metoda najde a vrátí item z pole Current, které se nachází na dané vizuální lokalitě (dané konkrétním bodem).
		/// Souřadnice daného bodu musí odpovídat souřadnicím položek v poli this.ElementVisibleList.
		/// Pokud si nadřízená aplikace souřadnice prvků upravuje 
		/// (což pravděpodobně činí, protože zdejší souřadnice mají počátek = bod 0/0 v počátku řádku, 
		/// ale volající aplikace kreslí řádek do jiného počátku = řádek putuje po parent controlu),
		/// pak musí volající aplikace reverzním způsobem převést souřadnici bodu tak, aby byla vztažena k počátku 0/0 tohoto řádku.
		/// Pokud je na daném bodě více prvků tohoto řádku, pak tato funkce vrací první z nich.
		/// Pokud na daném bodě není žádný prvek, vrací null.
		/// Pokud je na daném bodě více prvků, a chceme získat jejich seznam, je třeba volat metodu FindCurrentItemListByLocation().
		/// </summary>
		/// <param name="point"></param>
		/// <returns></returns>
		internal GridDataChartElementCls FindFirstCurrentItemByLocation(Point point)
		{
			List<GridDataChartElementCls> currentList = this.ElementVisibleList;
			foreach (GridDataChartElementCls item in currentList)
			{
				if (item.ElementArea.Contains(point))
					return item;
			}
			return null;
		}
		/// <summary>
		/// Metoda najde a vrátí seznam všech items z pole Current, které se nachází na dané vizuální lokalitě (dané konkrétním bodem).
		/// Souřadnice daného bodu musí odpovídat souřadnicím položek v poli this.ElementVisibleList.
		/// Pokud si nadřízená aplikace souřadnice prvků upravuje 
		/// (což pravděpodobně činí, protože zdejší souřadnice mají počátek = bod 0/0 v počátku řádku, 
		/// ale volající aplikace kreslí řádek do jiného počátku = řádek putuje po parent controlu),
		/// pak musí volající aplikace reverzním způsobem převést souřadnici bodu tak, aby byla vztažena k počátku 0/0 tohoto řádku.
		/// Pokud na daném bodě není žádný prvek, vrací prázdný list.
		/// </summary>
		/// <param name="point"></param>
		/// <returns></returns>
		internal List<GridDataChartElementCls> FindCurrentItemListByLocation(Point point)
		{
			List<GridDataChartElementCls> currentList = this.ElementVisibleList;
			List<GridDataChartElementCls> result = new List<GridDataChartElementCls>();
			foreach (GridDataChartElementCls item in currentList)
			{
				if (item.ElementArea.Contains(point))
					result.Add(item);
			}
			return result;
		}
		#endregion
		#region ROW FILTER
        /// <summary>
        /// Tento řádek sám je vybrán filtrem jako viditelný (osobně vyhovuje filtru).
        /// </summary>
        internal bool RowIsVisibleByFilter { get; set; }
		/// <summary>
		/// Vrátí true, když aktuální řádek vyhovuje danému filtru.
		/// </summary>
        /// <param name="rowFilter"></param>
        internal bool ConformToRowFilter(RowFilter rowFilter)
		{
            if (rowFilter == null) return true;

            // Tady se dostávám trochu na dřeň : Columns a jeho RowFilter jsou postaveny dynamicky (neomezený počet sloupců), 
            // kde sloupce jsou identifikovány hodnotou ItemName.
            // Tato hodnota je do sloupců vepsána při konstruování gridu: ve třídě GridPanelCls v metodě _GraphCreateOne(GraphDeclarationItemCls declItem),
            //  když se přidávají sloupce (metodou _GraphCreateOneAddColumn(...)) je deklarován klíčový název columnu.
            // Deklarují se tři sloupce: refer, name, timeAxis.
            // Z nich mohou pocházet filtry.
            // Pro první dva sloupce máme v řádku odpovídající data: this.RowItem.Refer, this.RowItem.Name. 
            // Jejich hodnoty lze snadno porovnat s filtrem.
            // Třetí sloupec (timeAxis) může nést filtr aplikovatelný obecněji, jeho filtrování ponecháme na majiteli řádku (metoda this.RowItem.ConformToRowFilter()).
            foreach (RowFilterItem filterItem in rowFilter)
            {
                switch (filterItem.ItemName.ToLower())
                {   // Projdu všechny položky filtru (odpovídající sloupcům):
                    case "refer":
                        if (!filterItem.ValueIsConform(this.RowItem.Refer))         // Pokud tomuto filtru (položka) naše hodnota Refer nevyhovuje, pak vrátí false (řádek nevyhovuje filtru).
                            return false;
                        break;
                    case "name":
                        if (!filterItem.ValueIsConform(this.RowItem.Name))
                            return false;
                        break;
                    case "timeaxis":
                        break;
                }
            }
            return true;
		}
		#endregion
		#region SUBCLASS _UseItem : slouží při přepočtech pozice Bottom při skládání elementů do vizuálního řádku
		/// <summary>
		/// _UseItem : slouží při přepočtech pozice Bottom při skládání elementů do vizuálního řádku
		/// </summary>
		private class _UseItem
		{
			internal _UseItem(TimeRange time, decimal bottom, decimal top)
			{
				this.Time = time;
				this.Bottom = bottom;
				this.Top = top;
			}
			public override string ToString()
			{
				return "Time=" + this.Time.ToString() + "; Bottom=" + this.Bottom.ToString() + "; Top=" + this.Top.ToString();
			}
			internal TimeRange Time { get; private set; }
			internal decimal Bottom { get; private set; }
			internal decimal Top { get; private set; }
		}
		#endregion
	}
	#endregion
	#region CLASS GridDataChartElementCls : vizuální reprezentace dat elementu
	/// <summary>
	/// GridDataChartElementCls : vizuální reprezentace dat elementu
	/// </summary>
	internal class GridDataChartElementCls : GraphElementDataCls
	{
		#region KONSTRUKCE A ZÁKLADNÍ PROPERTY (relativní dimenze Y, pozice Rectangle)
		internal GridDataChartElementCls(GridDataRowCls parent, IDataElement data)
			: base(data, parent.RowItem)
		{
			this.Parent = parent;
		}
		public override string ToString()
		{
			return "Element={" + this.DataElement.ToString() + "}; Bottom=" + this.RelativeBottom.ToString() + "; Top=" + this.RelativeTop.ToString();
		}
		/// <summary>
		/// Reference na parent objekt = vizuální řádek, kde je tento element evidován.
		/// Každý element je hostován v jednom logickém řádku (třídy GridDataRowCls),
		/// ale jeden řádek (GridDataRowCls) může být zobrazen ve více nodech (třída GridNodeRowCls).
		/// </summary>
		internal GridDataRowCls Parent { get; private set; }
		/// <summary>
		/// Metoda vloží do řádku nového parenta.
		/// Používá se v průběhu přesunu na jiný řádek.
		/// </summary>
		/// <param name="parent"></param>
		internal void SetParent(GridDataRowCls parent)
		{
			this.Parent = parent;
		}
		/// <summary>
		/// Barva pozadí elementu
		/// </summary>
		internal Color ElementBackColor { get { return this.DataElement.BackColor; } }
		/// <summary>
		/// Prostor elementu přes celou výšku řádku
		/// </summary>
		internal Rectangle ElementRowArea;
		/// <summary>
		/// Prostor elementu přes relativní souřadnice Y
		/// </summary>
        internal Rectangle ElementRelArea;
        /// <summary>
        /// Obsahuje (vrátí) true, pokud tento element potřebuje zvětšit svoji šířku (tj. když má kladný anebo nulový čas, ale nemá Image a jeho současná šířka ElementRelArea.Width je menší než 3 pixely
        /// </summary>
        internal bool NeedAddVisualWidth { get { return (this.TimeRange.Seconds >= 0D && this.ElementRelArea.Width < 3); } }
        /// <summary>
        /// Obsahuje (vrátí) true, pokud tento element nelze zobrazit (jeho čas je záporný)
        /// </summary>
        internal bool TimeInvisible { get { return (this.TimeRange.Seconds < 0D); } }
        /// <summary>
		/// Hodnota RelativeBottom, relativní (hodnota 1 = jedna celá kapacitní linka), nejde o pixely.
		/// Hodnota RelativeTop je vyšší než RelativeBottom.
		/// První linka dole má hodnotu RelativeBottom = 0, a RelativeTop rovnou jejímu UseRatio.
		/// </summary>
		internal decimal RelativeBottom { get; private set; }
		/// <summary>
		/// Hodnota RelativeTop, relativní (hodnota 1 = jedna celá kapacitní linka), nejde o pixely.
		/// Hodnota RelativeTop je vyšší než RelativeBottom.
		/// První linka dole má hodnotu RelativeBottom = 0, a RelativeTop rovnou jejímu UseRatio.
		/// </summary>
		internal decimal RelativeTop { get; private set; }
		/// <summary>
		/// Hodnota RelativeHeight, relativní (hodnota 1 = jedna celá kapacitní linka), nejde o pixely.
		/// Jde o rozdíl RelativeTop - RelativeBottom.
		/// </summary>
		internal decimal RelativeHeight { get { return this.RelativeTop - this.RelativeBottom; } }
		/// <summary>
		/// Příznak true, když se element nachází ve viditelné části časové osy.
		/// Hodnota je určena při přípravě seznamu viditelných elementů.
		/// </summary>
		internal bool IsInVisibleAxis { get; private set; }
		/// <summary>
		/// Příznak true, když jsou vypočteny všechny vizuální dimenze elementu.
		/// Hodnota je false pro elementy, které nejsou viditelné, a nebyly dosud kalkulované.
		/// Viditelné elementy jsou kalkulované automaticky, 
		/// neviditelné jsou kalkulovány při požadavku na nalezení a vydání takového elementu (viz metoda GridDataRowCls.GetChartElement()).
		/// </summary>
		internal bool IsAllDimensionCalculated { get; private set; }
		/// <summary>
		/// For internal use only!!!
		/// Set value to property "IsInVisibleAxis".
		/// </summary>
		/// <param name="value"></param>
		internal void SetVisibleAxis(bool value) { this.IsInVisibleAxis = value; }
		/// <summary>
		/// Vloží dané dimenze do svého Rectangle ElementRowArea = prostor přes celý řádek
		/// </summary>
		/// <param name="dimX"></param>
		/// <param name="dimY"></param>
		internal void StoreElementRowArea(OneDimensionI dimX, OneDimensionI dimY)
		{
			this.ElementRowArea = new Rectangle(dimX.Begin, dimY.Begin, dimX.Length, dimY.Length);
			this.IsAllDimensionCalculated = false;
		}
		/// <summary>
		/// Vloží dané dimenze do svého Rectangle ElementRelArea = prostor podle relativních souřadnic
		/// </summary>
		/// <param name="dimX"></param>
		/// <param name="dimY"></param>
		internal void StoreElementRelArea(OneDimensionI dimX, OneDimensionI dimY)
		{
			this.ElementRelArea = new Rectangle(dimX.Begin, dimY.Begin, dimX.Length, dimY.Length);
			this.IsAllDimensionCalculated = false;
		}
		/// <summary>
		/// Uloží do objektu jeho relativní pozici na ose Y (Bottom, Top).
		/// Souřadnice se uvádí v relativní hodnotě 0.00 - 1.00
		/// </summary>
		/// <param name="bottom"></param>
		/// <param name="top"></param>
		internal void StoreRelativeY(decimal bottom, decimal top)
		{
			this.RelativeBottom = bottom;
			this.RelativeTop = top;
		}
		/// <summary>
		/// Text pro kontrolu logické a fyzické pozice
		/// </summary>
		internal string DebugInfo
		{
			get
			{
				string eol = Environment.NewLine;
				string info = "ID=" + this.GId + "; Time=" + this.TimeRange + "; UseRatio=" + this.UseRatio + eol +
					"Bottom=" + this.RelativeBottom + "; Top=" + this.RelativeTop + eol +
					"ElementArea: {Left=" + this.ElementArea.Left + ",Width=" + this.ElementArea.Width + ",Top=" + this.ElementArea.Top + ", Height=" + this.ElementArea.Height + ", Bottom=" + this.ElementArea.Bottom + "}";
				return info;
			}
		}
		#endregion
		#region SPECIFICKÉ VÝPOČTY VNITŘNÍCH DIMENZÍ
		/// <summary>
		/// Zajistí kontrolu, zda jsou vypočteny interní dimenze elementu.
		/// Pokud jsou, nic nedělá.
		/// Pokud nejsou, vypočte je.
		/// </summary>
		internal void InternalDimensionCheck()
		{
			if (!this.IsAllDimensionCalculated)
				this.InternalDimensionCalculate();
		}
		/// <summary>
		/// Zajistí vypočtení vnitřních dimenzí elementu na základě jěho typu.
		/// </summary>
		internal void InternalDimensionCalculate()
		{
			if (this.DataElement.ElementLayer == GraphElementLayerType.TempLayer)
				this._PrepareVisualDataForTemp();
			else
			{
				switch (this.Parent.RowGraph)
				{
					case RowGraphMode.Gantt:
						this._PrepareVisualDataForGantt();
						break;
					case RowGraphMode.CapacityUtilization:
						this._PrepareVisualDataForGraphCapacity();
						break;
					case RowGraphMode.TaskCapacityLink:
						this._PrepareVisualDataForTaskLink();
						break;
				}
			}

			// Vypočteno:
			this.IsAllDimensionCalculated = true;
		}
		/// <summary>
		/// Připraví dimenze elementu pro element vrstvy Temporary.
		/// Takový element se vykreslí přes celou výšku řádku.
		/// </summary>
		private void _PrepareVisualDataForTemp()
		{
			// Element je připraven, vykreslí se plná velikost elementu z this.ElementArea, nic víc není zapotřebí:
			this.ElementArea = this.ElementRowArea;
		}
		/// <summary>
		/// Připraví dimenze elementu pro graf typu Gantt.
		/// V tuto chvíli je připravena hodnota do this.ElementArea, popisuje celý vnitřní prostor řádku (Y, height) a času (X, Width).
		/// </summary>
		private void _PrepareVisualDataForGantt()
		{
			// Gantt je připraven, vykreslí se plná velikost elementu z this.ElementArea, nic víc není zapotřebí:
			this.ElementArea = this.ElementRowArea;
		}
		/// <summary>
		/// Připraví dimenze elementu pro graf typu CapacityUtilization.
		/// V tuto chvíli je připravena hodnota do this.ElementArea, popisuje celý vnitřní prostor řádku (Y, height) a času (X, Width).
		/// </summary>
		private void _PrepareVisualDataForGraphCapacity()
		{
			this.ElementArea = this.ElementRowArea;           // Prostor na výšku celého řádku.

			float standardUsage = 0.75F;

			int x = this.ElementArea.X;
			int w = this.ElementArea.Width;
			int b = this.ElementArea.Bottom;

			// a) pozadí kapacity :
			float standardHeight = standardUsage * (float)this.ElementArea.Height;         // Výška elementu, který zobrazuje 100%
			int height = (int)standardHeight;
			this.RectangleBack = new Rectangle(x, b - height, w, height);

			// b) ratio:
			float utilizationHeight = this.DataElement.UtilizationRatio * standardHeight;  // Výška elementu, který zobrazuje (UtilizationRatio)%
			height = (int)utilizationHeight;
			int y = b - height;
			this.ElementArea = new Rectangle(x, y, w, height);

			// c) top souřadnice vrcholku UtilizationRatio, relativně k this.ElementArea
			this.CapacityUsedTop = y;
		}
		/// <summary>
		/// Připraví dimenze elementu pro graf typu TaskCapacityLink.
		/// V tuto chvíli je připravena hodnota do this.ElementArea, popisuje celý vnitřní prostor řádku (Y, height) a času (X, Width).
		/// </summary>
		private void _PrepareVisualDataForTaskLink()
		{
			// Tento graf zahrnuje dva typy objektů, odlišené hodnotou this.DataElement.ElementLayer :
			switch (this.DataElement.ElementLayer)
			{
				case GraphElementLayerType.SubLayer:
					// Stav kapacity - vykresluje se jako poloprůhledné pozadí grafu, 
					//  v metodě GridGraphCls._PaintRowGraphCapacityBase, z dat v RectangleBack:
					this.ElementArea = this.ElementRowArea;                   // Prostor na výšku celého řádku.
					this.RectangleBack = this.ElementRowArea;
					break;
				case GraphElementLayerType.ItemLayer:
					// Vlastní položka grafu - vykresluje se v prostoru relativních souřadnic:
					Rectangle elementArea = this.ElementRelArea;              // Relativní výška elementu
					if (this.CurrentlyTransposed)                             // Pokud tento element právě přesouvám po cizím řádku:
						elementArea.Y = 1;                                    //  pak bude vykreslen úplně na horním okraji řádku.
					this.ElementArea = elementArea;
					break;
			}
		}
		/// <summary>
		/// Vrátí true, pokud se element má vložit do seznamu viditelných elementů, v závislosti na šířce.
		/// Element spadá do viditelného časového intervalu.
		/// Pokud je jeho šířka menší než (???) nebo rovna nule, pak se jeho přidávání řídí jinými pravidly.
		/// Aktuální dimenze jsou uvedeny v this.ElementRowArea a this.ElementRelArea.
		/// Dimenze X jsou v obou stejné.
		/// </summary>
		/// <returns></returns>
		internal bool AddToVisibleByWidth()
		{
			// Do viditelných položek dám položky širší než 0:
			if (this.ElementRowArea.Width >= 0) return true;
			//   ... a trojúhelníky:
			if (this.DataElement.ElementShapeType == GraphElementShapeType.RightTriangle) return true;
            //   ... a elementy s ikonou not null:
            if (this.DataElement.Icon != null) return true;
			//   a nic více:
			return false;
		}
		#endregion
		#region PODPORA EDITACE
		/// <summary>
		/// Nastaví hodnotu IsInEditingState
		/// </summary>
		/// <param name="editingState"></param>
		internal void SetEditingState(bool editingState)
		{
			this.IsInEditingState = editingState;
		}
		/// <summary>
		/// Metoda vymaže ty svoje vlastní data, která přestávají platit po transpozici do jiného řádku.
		/// </summary>
		internal void ClearOnTranspose()
		{
			this.DetailArea = Rectangle.Empty;
			this.ElementArea = Rectangle.Empty;
			this.RectangleBack = Rectangle.Empty;
			this.VisualRectangle = Rectangle.Empty;
		}

		#endregion
	}
	#endregion
	#region CLASS GridDataVisualLineCls : pracovní dočasné podklady pro fyzické zobrazování dat (soupis jen viditelných řádků, vypočtené ikony, pozice řádků, výška, Rectangle)
	/// <summary>
	/// GridDataVisualLineCls : podklady pro fyzické zobrazování dat (soupis jen viditelných řádků, vypočtené ikony, pozice řádků, výška, Rectangle).
	/// Jeden záznam popisuje jeden viditelný řádek. Po zneviditelnění řádku (nadřízený node se zavře) tento objekt z paměti zmizí.
	/// Pokud je jeden fyzický řádek zobrazen ve více vizuálních řádcích, existuje více instancí této třídy GridDataVisualLineCls.
	/// Tato třída tedy popisuje konkrétní umístění dat řádku v prostoru grafu
	/// Po každém načtení dalších dat, po každém otevření / zavření nodu se znovu celý seznam Rows[] vypočítá.
	/// </summary>
	internal class GridDataVisualLineCls
	{
		#region PROMĚNNÉ
		private int _RowIdx;
		private RowID _RowId;
		private GridDataRowCls _DataRow;
		private int _Top;
		private TreeLineType _IconAdded = TreeLineType.None;
		public override string ToString()
		{
			GridDataRowCls nodeData = this._DataRow;
			return "[" + this.RowIdx.ToString() + "] = " + this.RowId.ToString() + ": " + this.DataRow.RowItem.Refer + " " + this.DataRow.RowItem.Name + "; Elements:" + this.DataRow.ElementCount;
		}
		#endregion
		#region PROPERTY
		/// <summary>
		/// Index řádku.
		/// Je jednoznačný jen v rámci jednoho vytvořeného seznamu Rows.
		/// Seznam Rows se vytváří po každém načtení dat, po každém otevření/zavření nodu a po každé změně filtrování.
		/// Seznam obsahuje řádky v nepřerušované řadě { 0 - max }, kde RowIdx odpovídá indexu v seznamu Rows.
		/// Jakkoliv nalezený řádek tedy v sobě nese informaci o své pozici, a umožní tak najít i své sousední řádky.
		/// </summary>
		internal int RowIdx { get { return this._RowIdx; } }
		/// <summary>
		/// RowID řádku = kombinace GID a NodeId.
		/// </summary>
		internal RowID RowId { get { return this._RowId; } }
		/// <summary>
		/// ID nodu. Jde o trvalý klíč nodu v mapě stromu. ID nodu je permanentní a unikátní.
		/// ID nodu je klíčem ke všem akcím na vizuální úrovni, pro vizuální práci nelze využívat GID 
		/// (to je z vizuálního hlediska nejednoznačné: jeden GID se může zobrazit na více řádcích, pro různé parenty).
		/// </summary>
		internal int NodeId { get { return this._RowId.NodeId; } }
		/// <summary>
		/// GID dat v tomto řádku.
		/// Trvalá konstanta, odkazuje se na existující data v databázi.
		/// Nezmění se ani přenačtením dat, ani změnami seznamu.
		/// Ukazuje na logická data řádku, ale nelze používat k orientaci v mapě Tree, na to je třeba používat NodeId.
		/// </summary>
		internal GID DataGId { get { return this._DataRow.GId; } }
		/// <summary>
		/// Pracovní data z datové vrstvy grafu. Je to o jednu vrstvu výše než jsou data z DataSource.
		/// Tato data jsou permanentní, po načtení z DataSource se nemění.
		/// Nikdy není null (záznam this se vytváří pouze na základě not null podkladových dat).
		/// </summary>
		internal GridDataRowCls DataRow { get { return this._DataRow; } }
		/// <summary>
		/// Pozice Top tohoto řádku v absolutní souřadnici. První řádek (s indexem 0) má Top = 0, 
		/// všechny další řádky mají svůj Top = (Minulý řádek.Top + .Height).
		/// Jde o jednoznačnou pozici řádku ve virtuálním dlouhém seznamu, z něhož se zobrazuje jen určitý výsek.
		/// </summary>
		internal int Top { get { return this._Top; } set { this._Top = value; } }
		/// <summary>
		/// Výška tohoto řádku. Fyzicky je uložena v this.DataRow.RowHeightPx.
		/// Pozor: čtení této property se odkazuje na property HeightPx z podkladového datového řádku 
		/// a může tedy způsobit přepočet pozic elementů v podkladovém řádku.
		/// Nikoli načítání z datového zdroje.
		/// </summary>
		internal int HeightPx { get { return (this._DataRow != null ? this._DataRow.RowHeightPx : 0); } }
		/// <summary>
		/// Vypočítaná pozice Bottom tohoto řádku.
		/// Pozor: čtení této property se odkazuje na property HeightPx z podkladového datového řádku 
		/// a může tedy způsobit přepočet pozic elementů v podkladovém řádku.
		/// Nikoli načítání z datového zdroje.
		/// Jde o pozici pixelu, na který de facto už se žádný objekt tohoto řádku nekreslí. Tak je to v grafice zvykem.
		/// </summary>
		internal int Bottom { get { return this._Top + this.HeightPx; } }
		/// <summary>
		/// Ikony Tree tohoto řádku. Odpovídají pozici řádku v Tree, a odpovídají otevření/zavření nodu.
		/// </summary>
		internal TreeLineType[] IconTypes;
		/// <summary>
		/// Ikona stromu, která se výjimečně kreslí na místě začátku nodu. Obsahuje buď TreeLineType.None, anebo TreeLineType.ExpandedParent.
		/// Hodnota TreeLineType.ExpandedParent je počáteční vodící linkou k prvnímu ChildNodu, protože u řádků s velkou Height by to bez ní vypadalo blbě.
		/// </summary>
		internal TreeLineType IconAdded { get { return this._IconAdded; } }
		/// <summary>
		/// Plný konstruktor
		/// </summary>
		/// <param name="rowIdx">Index řádku</param>
		/// <param name="nodeId">ID nodu, který je zde zobrazen</param>
		/// <param name="dataRow">Data řádku</param>
		/// <param name="nodeTop">Top souřadnice</param>
		/// <param name="prevIcon">Předcházející ikony Tree</param>
		/// <param name="currIcon">Aktuální ikona Tree</param>
		/// <param name="addIcon">Přídavná ikona Tree (typicky TreeLineType.None, anebo TreeLineType.ExpandedParent)</param>
		internal GridDataVisualLineCls(int rowIdx, int nodeId, GridDataRowCls dataRow, int nodeTop, List<TreeLineType> prevIcon, TreeLineType currIcon, TreeLineType addIcon)
		{
			this._RowIdx = rowIdx;
			this._RowId = new RowID(dataRow.GId, nodeId);
			this._DataRow = dataRow;
			this._Top = nodeTop;
			this.IconTypes = new TreeLineType[prevIcon.Count + 1];
			prevIcon.CopyTo(this.IconTypes, 0);
			this.IconTypes[prevIcon.Count] = currIcon;
			this._IconAdded = addIcon;
		}
		/// <summary>
		/// Prázdný konstruktor
		/// </summary>
		/// <param name="currIcon"></param>
		internal GridDataVisualLineCls(TreeLineType currIcon)
		{
			this._RowIdx = -1;
			this._DataRow = null;
			this._Top = -1;
			this.IconTypes = new TreeLineType[0];
			this._IconAdded = TreeLineType.None;
		}
		#endregion
	}
	#endregion
	#region STRUCT OneDimensionI : struktura Begin, Length, End. Integer.
	/// <summary>
	/// OneDimensionI : struktura Begin, Length, End. Integer.
	/// </summary>
	internal struct OneDimensionI
	{
		public OneDimensionI(int begin, int length)
		{
			this._Begin = begin;
			this._Length = length;
		}
		private int _Begin;
		private int _Length;
		public override string ToString()
		{
			return "Begin=" + this.Begin + "; Length=" + this.Length;
		}
		/// <summary> { get; set; }
		/// Počáteční bod
		/// </summary>
		public int Begin { get { return _Begin; } set { _Begin = value; } }
		/// <summary> { get; set; }
		/// Délka
		/// </summary>
		public int Length { get { return _Length; } set { _Length = value; } }
		/// <summary> { get; }
		/// Koncový bod = Begin + Length
		/// </summary>
		public int End { get { return _Begin + _Length; } }
		/// <summary> { get; }
		/// Příznak prázdnosti dimenze (0,0)
		/// </summary>
		public bool IsEmpty { get { return (this._Begin == 0 && this._Length == 0); } }
		/// <summary> { get; }
		/// Obsahuje Empty prvek
		/// </summary>
		public static OneDimensionI Empty { get { return new OneDimensionI(0, 0); } }
	}
	#endregion
}
